import { Base64 } from 'js-base64';
import md5 from 'js-md5';

/**
 * author: liujie
 */

let templateSettings = {
    evaluate: /<%([\s\S]+?)%>/g,
    interpolate: /<%=([\s\S]+?)%>/g,
    escape: /<%-([\s\S]+?)%>/g
};
let escapes = {
    '\'': '\'',
    '\\': '\\',
    '\r': 'r',
    '\n': 'n',
    '\u2028': 'u2028',
    '\u2029': 'u2029'
};
let noMatch = /(.)^/;
let escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;

let escapeChar = function (match) {
    return '\\' + escapes[match];
};
let escapeMap = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    '\'': '&#x27;',
    '`': '&#x60;'
};
let createEscaper = function (map) {
    let escaper = function (match) {
        return map[match];
    };
    // Regexes for identifying a key that needs to be escaped.
    let source = '(?:' + Object.keys(map).join('|') + ')';
    let testRegexp = RegExp(source);
    let replaceRegexp = RegExp(source, 'g');
    return function (string) {
        string = string === null ? '' : '' + string;
        return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
    };
};
let appUtil = {
    isEmpty: isEmpty,
    now: function () {
        return Date.now() || new Date().getTime();
    },
    clone: function (obj) {
        if (!appUtil.isObject(obj)) { return obj; }
        return appUtil.isArray(obj) ? obj.slice() : Object.assign({}, obj);
    },
    getLocal: function (path, encoded) {
        return getStorage(path, encoded, 'localStorage');
    },
    setLocal: function (name, data, shouldEncode) {
        if (type(name) !== '[object String]') {
            throw TypeError('name应为字符串！');
        }
        setStorage(name, data, shouldEncode, 'localStorage');
    },
    getSes: function (path, encoded) {
        return getStorage(path, encoded, 'sessionStorage');
    },
    setSes: function (name, data, shouldEncode) {
        if (type(name) !== '[object String]') {
            throw TypeError('name应为字符串！');
        }
        setStorage(name, data, shouldEncode, 'sessionStorage');
    },
    getParamFromUrl: function (url, key) {
        // 如果没有传url，默认为当前页面的url
        if (key === undefined) {
            key = url;
            url = window.location.search;
        }
        url = url.split('?')[1];
        if (!url) {
            return;
        } else if (url.indexOf('#') > -1) {
            url = url.split('#')[0];
        }
        try {
            let result = {};
            let keys = appUtil.isArray(key) ? key : null;
            url.split('&').forEach(function (item) {
                // key不是数组，因此keys的值为null，也就是只提取一个参数
                if (!keys) {
                    if (item.split('=')[0] === key) {
                        result = window.decodeURIComponent(item.split('=')[1]);
                    }
                } else {
                    keys.forEach(function (param) {
                        if (item.split('=')[0] === param) {
                            result[param] = window.decodeURIComponent(item.split('=')[1]);
                        }
                    });
                }
            });
            return result;
        } catch (e) {
            return;
        }
    },
    base64: {
        encode: function (data) {
            return Base64.encode(data);
        },
        decode: function (data) {
            return Base64.decode(data);
        }
    },
    md5: md5,
    // 根据路径生成项目内唯一GUID，这里的path为某文件相对src的路径
    getGUIDByPath: function (path) {
        return md5(path);
    },
    formatDate: function (date, formatter) {
        return formatDate(date, formatter);
    },
    forEach: function (obj, iteratee) {
        let i, length;
        if (isArrayLike(obj)) {
            for (i = 0, length = obj.length; i < length; i++) {
                iteratee(obj[i], i, obj);
            }
        } else {
            let keys = Object.keys(obj);
            for (i = 0, length = keys.length; i < length; i++) {
                iteratee(obj[keys[i]], keys[i], obj);
            }
        }
        return obj;
    },
    map: function (obj, iteratee) {
        let keys = !isArrayLike(obj) && Object.keys(obj),
            length = (keys || obj).length,
            results = Array(length);
        for (let index = 0; index < length; index++) {
            let currentKey = keys ? keys[index] : index;
            results[index] = iteratee(obj[currentKey], currentKey, obj);
        }
        return results;
    },
    filter: function (obj, predicate) {
        let results = [];
        this.forEach(obj, function (value, index, list) {
            if (predicate(value, index, list)) results.push(value);
        });
        return results;
    },
    every: function (obj, predicate) {
        let keys = !isArrayLike(obj) && Object.keys(obj),
            length = (keys || obj).length;
        for (let index = 0; index < length; index++) {
            let currentKey = keys ? keys[index] : index;
            if (!predicate(obj[currentKey], currentKey, obj)) return false;
        }
        return true;
    },
    some: function (obj, predicate) {
        let keys = !isArrayLike(obj) && Object.keys(obj),
            length = (keys || obj).length;
        for (let index = 0; index < length; index++) {
            let currentKey = keys ? keys[index] : index;
            if (predicate(obj[currentKey], currentKey, obj)) return true;
        }
        return false;
    },
    random: function (min, max) {
        if (!max && max !== 0) {
            max = min;
            min = 0;
        }
        return min + Math.floor(Math.random() * (max - min + 1));
    },
    sample: function (obj, n) {
        if (n === null || n === undefined) {
            if (!isArrayLike(obj)) obj = this.values(obj);
            return obj[this.random(obj.length - 1)];
        }
        let sample = isArrayLike(obj) ? appUtil.clone(obj) : obj.values();
        let length = sample.length;
        n = Math.max(Math.min(n, length), 0);
        let last = length - 1;
        for (let index = 0; index < n; index++) {
            let rand = this.random(index, last);
            let temp = sample[index];
            sample[index] = sample[rand];
            sample[rand] = temp;
        }
        return sample.slice(0, n);
    },
    isElement: isElement,
    isArray: isArray,
    isArrayLike: isArrayLike,
    isObject: isObject,
    routerUrl: routerUrl,
    GetQueryString: GetQueryString,
    strIntercept: strIntercept,
    generateHTMLElement: function (htmlTemplate, params) {
        let reg = /\{\{(.+?)\}\}/g;
        if (appUtil.isArray(params)) {
            let fragment = document.createDocumentFragment();
            for (let i = 0; i < params.length; i++) {
                let temp = htmlTemplate.replace(reg, function (full, key) {
                    return params[i][key.trim()] || '';
                });
                let doc = new DOMParser().parseFromString(temp, 'text/html');
                fragment.appendChild(doc.body.childNodes[0]);
            }
            return fragment;
        } else {
            htmlTemplate = htmlTemplate.replace(reg, function (full, key) {
                return params[key.trim()] || '';
            });
            let doc = new DOMParser().parseFromString(htmlTemplate, 'text/html');
            return doc.body.childNodes[0];
        }
    },
    escape: createEscaper(escapeMap),
    template: function (text, settings) {
        settings = Object.assign({}, settings, templateSettings);

        // Combine delimiters into one regular expression via alternation.
        let matcher = RegExp([
            (settings.escape || noMatch).source,
            (settings.interpolate || noMatch).source,
            (settings.evaluate || noMatch).source
        ].join('|') + '|$', 'g');

        // Compile the template source, escaping string literals appropriately.
        let index = 0;
        let source = '__p+=\'';
        text.replace(matcher, function (match, escape, interpolate, evaluate, offset) {
            source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
            index = offset + match.length;

            if (escape) {
                source += '\'+\n((__t=(' + escape + '))==null?\'\':appUtil.escape(__t))+\n\'';
            } else if (interpolate) {
                source += '\'+\n((__t=(' + interpolate + '))==null?\'\':__t)+\n\'';
            } else if (evaluate) {
                source += '\';\n' + evaluate + '\n__p+=\'';
            }

            // Adobe VMs need the match returned to produce the correct offset.
            return match;
        });
        source += '\';\n';

        // If a variable is not specified, place data values in local scope.
        if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';

        source = 'var __t,__p=\'\',__j=Array.prototype.join,' +
            'print=function(){__p+=__j.call(arguments,\'\');};\n' +
            source + 'return __p;\n';

        let render;
        try {
            render = new Function(settings.variable || 'obj', this, source);
        } catch (e) {
            e.source = source;
            throw e;
        }

        let template = function (data) {
            return render.call(this, data);
        };

        // Provide the compiled source as a convenience for precompilation.
        let argument = settings.variable || 'obj';
        template.source = 'function(' + argument + '){\n' + source + '}';

        return template;
    },
    createFunction: function (code, params) {
        let keys = Array.prototype.slice.call(Object.keys(params || {}));
        return new Function(...keys, code);
    },
    getCookie: function (cookieName) {
        let arrStr = document.cookie.split('; ');
        for (let i = 0; i < arrStr.length; i++) {
            let temp = arrStr[i].split('=');
            if (temp[0] === cookieName) {
                return decodeURI(temp[1]);
            }
        }
        return null;
    },
    setCookie: function (name, value, expires, path) {
        let Days = 30 * 1;
        let exp = new Date();
        exp.setTime(exp.getTime() + expires || Days * 24 * 60 * 60 * 1000);
        document.cookie = name + '=' + encodeURI(value) + ';expires=' + exp.toGMTString() + path;
    },
    browser: {
        /**
         * 获取浏览器类型
         */
        type: function () {
            if (!!window.ActiveXObject || 'ActiveXObject' in window) {
                return 'IE';
            }

            if ((window.navigator.userProfile + '') === 'undefined') {
                return '360';
            }

            let userAgent = navigator.userAgent;
            //判断是否Opera浏览器
            if (userAgent.indexOf('Opera') > -1) {
                return 'Opera';
            }
            //判断是否Firefox浏览器
            if (userAgent.indexOf('Firefox') > -1) {
                return 'FF';
            }
            if (userAgent.indexOf('Chrome') > -1) {
                return 'Chrome';
            }
            //判断是否Safari浏览器
            if (userAgent.indexOf('Safari') > -1) {
                return 'Safari';
            }
            //判断是否IE浏览器，避免对新版IE的漏检
            if (userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1 && !this.browser.isOpera()) {
                return 'IE';
            }
        },
        /**
         * 是否为 IE
         */
        isIE: function () {
            return this.type() === 'IE';
        },
        getIEVsersion: function () {
            let userAgent = navigator.userAgent;
            let isIE = userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1;
            let isEdge = userAgent.indexOf('Edge') > -1 && !isIE;
            let isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf('rv:11.0') > -1;

            if (this.browser.isIE()) {
                let reIE = new RegExp('MSIE (\\d+\\.\\d+);');
                reIE.test(userAgent);
                let fIEVersion = parseFloat(RegExp['$1']);
                if (fIEVersion === 7) {
                    return '7';
                } else if (fIEVersion === 8) {
                    return '8';
                } else if (fIEVersion === 9) {
                    return '9';
                } else if (fIEVersion === 10) {
                    return '10';
                } else {
                    // IE6之前的版本不再判断，全部返回6
                    return '6';
                }
            } else if (isEdge) {
                return 'edge';
            } else if (isIE11) {
                return '11';
            } else {
                return '-1';
            }
        },
        isOpera: function () {
            return this.type() === 'Opera' ? true : false;
        }
    },
    stopBubble: function (e) {
        e = window.event || e;
        if (e.stopPropagation) {
            e.stopPropagation();
        } else {
            e.cancelBubble = true;
        }
    },
    /**
     * 为url拼接参数，传入的params是个对象，由需要拼接的参数名:参数值构成
     * 只在该参数不存在(判断依据为key是否存在，如&name=&也算name存在)时进行拼接
     */
    mixinParamsToUrl: function (url, params, forceRewrite) {
        url = url + '';

        let useQuestionMask = true;
        if (url.indexOf('?') > -1) {
            useQuestionMask = false;
        }

        for (let key in params) {
            if (params.hasOwnProperty(key)) {
                let val = this.getParamFromUrl(url, key);
                if (val === undefined) {
                    url += (useQuestionMask ? '?' : '&') + key + '=' + encodeURI(params[key]);
                } else {
                    if (forceRewrite) {
                        // 需要对参数对进行整体替换，因为val的值也可能出现在其他参数里
                        url.replace(key + '=' + val, key + '=' + params[key]);
                    }
                }

                if (useQuestionMask) {
                    useQuestionMask = false;
                }
            }
        }
        return url;
    },
    throttle: function (func, wait, options) {
        let timeout, context, args, result;
        let previous = 0;
        if (!options) options = {};

        let later = function () {
            previous = options.leading === false ? 0 : appUtil.now();
            timeout = null;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
        };

        let throttled = function () {
            let now = appUtil.now();
            if (!previous && options.leading === false) previous = now;
            let remaining = wait - (now - previous);
            context = this;
            args = arguments;
            if (remaining <= 0 || remaining > wait) {
                if (timeout) {
                    clearTimeout(timeout);
                    timeout = null;
                }
                previous = now;
                result = func.apply(context, args);
                if (!timeout) context = args = null;
            } else if (!timeout && options.trailing !== false) {
                timeout = setTimeout(later, remaining);
            }
            return result;
        };

        throttled.cancel = function () {
            clearTimeout(timeout);
            previous = 0;
            timeout = context = args = null;
        };

        return throttled;
    },
    debounce: function (func, wait, immediate) {
        let timeout, result;

        let later = function (context, args) {
            timeout = null;
            if (args) result = func.apply(context, args);
        };

        let debounced = restArguments(function (args) {
            if (timeout) clearTimeout(timeout);
            if (immediate) {
                let callNow = !timeout;
                timeout = setTimeout(later, wait);
                if (callNow) result = func.apply(this, args);
            } else {
                timeout = delay(later, wait, this, args);
            }

            return result;
        });

        debounced.cancel = function () {
            clearTimeout(timeout);
            timeout = null;
        };

        return debounced;
    },
    convertArrayToTree(arrayData, options) {
        // 深拷贝arrayData，将当前函数变为纯函数，消除副作用
        const copyArrayData = JSON.parse(JSON.stringify(arrayData));
        // 默认以数组元素的id和parentId属性确定父子关系，可以传入options覆盖这个规则
        const defaultOptions = {
            id: 'id',
            parentId: 'parentId',
            level: 'level'
        };
        options = Object.assign({}, defaultOptions, options);
        // 保存任何有子树的节点，以其id为key，value是它的子树
        const childTreeMap = {};
        const canSort = !isEmpty((copyArrayData[0] && copyArrayData[0][options.level]));
        // 按照节点的级次升序排列，提高树结构转换效率（不提供level也可以正常转换，此时效率会略低一些）
        const sortedArrayData = canSort ? copyArrayData.sort((obj1, obj2) => {
            return parseInt(obj1[options.level]) - parseInt(obj2[options.level]);
        }) : copyArrayData;
        // 依次遍历数组每一项，确定任意两个元素的父子关系，保存在各自的children和parent属性内
        for (let i = sortedArrayData.length - 1; i >= 0; i--) {
            // 可以按照level排序时，不需要检查位于当前元素后面的元素，否则需要对整个数组检查
            for (let j = canSort ? j - 1 : sortedArrayData.length - 1; j >= 0; j--) {
                if (sortedArrayData[j][options.id] === sortedArrayData[i][options.parentId]) {
                    sortedArrayData[j].children = sortedArrayData[j].children || [];
                    sortedArrayData[j].children.unshift(sortedArrayData[i]);
                    sortedArrayData[i].parentNode = sortedArrayData[j];
                    break;
                }
            }
        }
        // 构建子树谱，并设置叶子节点标志
        for (let k = sortedArrayData.length - 1; k >= 0; k--) {
            if (sortedArrayData[k].children) {
                sortedArrayData[k].isLeaf = false;
                childTreeMap[sortedArrayData[k][options.id]] = sortedArrayData[k].children;
            } else {
                sortedArrayData[k].children = [];
                sortedArrayData[k].isLeaf = true;
            }
        }
        return {
            treeMap: sortedArrayData.filter(obj => {
                return !obj.parentNode;
            }),
            childTreeMap: childTreeMap,
            sortedArrayData: sortedArrayData
        };
    },
    getTreeNodesByLevel(tree, level, children = 'children') {
        if (!level >= 1) {
            throw new RangeError('invalid level, make sure that level >= 1');
        } else if (!isArray(tree) && !isArrayLike(tree)) {
            throw new TypeError('invalid tree input, the first param should be an Array or ArrayLike');
        }

        let result = [];
        function reduceTree(treeData, currentLevel, children) {
            let reducedMenuTree = treeData;
            // 如果仍然没有到达目标级次，则继续处理
            if (currentLevel > 1) {
                // 将树层级降一级，使用call是为了兼容类数组结构，如NodeLists类型的树结构
                reducedMenuTree = Array.prototype.map.call(treeData, menu => menu[children]).reduce((total, current) => { return total.concat(current); }, []);
                reduceTree(reducedMenuTree, --currentLevel, children);
            } else {
                result = reducedMenuTree;
            }
        }
        reduceTree(tree, level, children);
        return result;
    }
};

appUtil.forEach(['Arguments', 'Boolean', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function (name) {
    appUtil['is' + name] = function (obj) {
        return toString.call(obj) === '[object ' + name + ']';
    };
});

/**
 * 以下为内部函数
 */

function isEmpty(target, shouldTrim = true) {
    if (shouldTrim) {
        target = type(target) === '[object String]' ? target.trim() : target;
    }
    return target === null || target === undefined || target === '';
}

function isElement(obj) {
    return !!(obj && obj.nodeType === 1);
}

function isArray(obj) {
    return toString.call(obj) === '[object Array]';
}

function isObject(obj) {
    let type = typeof obj;
    return type === 'function' || type === 'object' && !!obj;
}

function type(target) {
    return toString.call(target);
}

function isArrayLike(collection) {
    let MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
    let length = collection.length;
    return typeof length === 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
}

// 转换eval方法，避免eslint报错
function evil(fn) {
    let Fn = Function;
    return new Fn('return ' + fn)();
};

// 修改地址栏参数
function changeURLArg(url, arg, arg_val) {
    const replaceText = arg + '=' + arg_val;
    if (url.match(arg + '=([^&]*)')) {
        return url.replace(evil('/(' + arg + '=)([^&]*)/gi'), replaceText);
    } else {
        if (url.match('[\?]')) {
            return url + '&' + replaceText;
        } else {
            return url + '?' + replaceText;
        }
    }
};

// 删除地址栏某个属性后的所有内容
function deleLast(url, arg) {
    if (!url.match(arg + '=([^&]*)') || !url || !url.match('[\?]')) {
        return url;
    }
    return url.replace(evil('/(&' + arg + '=)([^$]*)/gi'), '');
}

// 替换掉富文本的标签字符
function strIntercept(text) {
    return text.replace(/<[^>]+>/g, ' ').replace(/\r|\n|\s/g, '').replace(/[&nbsp;]|[&mdash;]|[&rdquo;]|[&ldquo;]/g, '');
}

// 控制切换后地址栏导航
function urlChange(data) {
    let nurl = [];
    nurl[0] = data;
    localStorage.urlInfo = JSON.stringify(nurl);
}

// 打开新页面跳转
function routerUrl(type, Parameters, title, icur) {
    let dashboard;
    urlChange({ title, icur });
    switch (type) {
        case 0:// 首页
            dashboard = '3376793774169209395';
            break;
        case 1:// 列表页
            dashboard = '3402684105567240718';
            break;
        case 2:// 详情页
            dashboard = '3402684882369118790';
            break;
        default:
            dashboard = '3376793774169209395';
            break;
    }
    let href = changeURLArg(location.href, 'dashboard', dashboard);
    if (type === 0) {
        href = deleLast(href, 'categoryId');
    }
    // window.location.href = changeURLArg(location.href, 'dashboard', dashboard);
    for (let keys in Parameters) {
        href += `&${keys}=${Parameters[keys]}`;
    }
    const a = document.createElement('a');
    a.href = href;
    a.target = '_blank';
    a.click();
};

// 3376793774169209395 首页
// 3402684105567240718 列表页
// 3402684882369118790 详情页
// http://127.0.0.1:8080/portal/system.html#/?page=3402513062521209137&dashboard=3376793774169209395

// 获取地址栏参数
function GetQueryString(name) {
    const href = location.href;
    if (!href.match(name + '=([^&]*)') || !href || !href.match('[\?]')) {
        return null;
    };
    return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(href) || [''])[1].replace(/\+/g, '%20')) || null;
};

/**
 * @param {*} path string或array，参数路径，字符串的格式为'user.name'，数组格式为[{'user': false}, {'name': true}]。
 *                 如果传入字符串，函数第二个参数(encoded)可以指定根路径（user）是否被编码，如果需要精确控制，请传数组格式。
 *                 数组格式下，每个成员对象的key为本级路径名，value为是否被编码
 * @param {*} encoded boolean，表示是否被编码，默认为已编码
 * @param {*} storageType string，存储类型，包括'localStorage'和'sessionStorage'
 */
function getStorage(path, encoded, storageType) {
    if (encoded === undefined) {
        encoded = true;
    }
    try {
        if (type(path) === '[object String]') {
            let pathArray = path.split('.');
            let result = window[storageType].getItem(pathArray[0]);
            if (!result) {
                return result;
            }

            result = encoded ? Base64.decode(result) : result;
            try {
                JSON.parse(result);
            } catch (e) {
                return result;
            }
            result = /^[0-9]*$/.test(result) ? result : JSON.parse(result);
            if (pathArray.length === 1) {
                return result;
            }

            for (let i = 1; i < pathArray.length; i++) {
                result = result[pathArray[i]];
            }
            return result;
        } else if (type(path) === '[object Array]') {
            let pathName = Object.keys(path[0])[0];
            let result = path[0][pathName] ? Base64.decode(window[storageType].getItem(pathName)) :
                window[storageType].getItem(pathName);

            result = /^[0-9]*$/.test(result) ? result : JSON.parse(result);
            for (let i = 1; i < path.length; i++) {
                pathName = Object.keys(path[i])[0];
                result = path[i][pathName] ? Base64.decode(result[pathName]) : result[pathName];
            }
            return result;
        }
    } catch (e) {
        return;
    }
}

/**
 * @param {*} name string，要存储的参数名
 * @param {*} data all，要存储的数据
 * @param {*} shouldEncode boolean，是否需要Base64编码，默认编码
 * @param {*} storageType string，存储类型，'localStorage'或'sessionStorage'
 */
function setStorage(name, data, shouldEncode, storageType) {
    if (appUtil.isEmpty(shouldEncode)) {
        shouldEncode = true;
    }
    if (type(data) !== '[object String]') {
        data = JSON.stringify(data);
    }
    window[storageType].setItem(name, shouldEncode ? Base64.encode(data) : data);
}

/**
 * 时间格式化函数
 * @param {*} date string或Date类型，需要格式化的时间，只要可以被new Date()解析即可
 * @param {*} formatter string，格式化要求，参考格式'yyyy-MM-dd hh:mm:ss'，函数通过替换
 *                      字符串中的占位字符对时间进行格式化
 */
function formatDate(date, formatter) {
    let normalDate = new Date(date);
    if (normalDate !== 'Invalid Date') {
        let year = normalDate.getFullYear().toString();
        let month = normalDate.getMonth() + 1;
        month = month > 9 ? month.toString() : '0' + month.toString();
        let day = normalDate.getDate();
        day = day > 9 ? day.toString() : '0' + day.toString();

        let hours = normalDate.getHours();
        hours = hours > 9 ? hours.toString() : '0' + hours.toString();
        let minutes = normalDate.getMinutes();
        minutes = minutes > 9 ? minutes.toString() : '0' + minutes.toString();
        let seconds = normalDate.getSeconds();
        seconds = seconds > 9 ? seconds.toString() : '0' + seconds.toString();

        return formatter.replace('yyyy', year).replace('MM', month).replace('dd', day).replace('hh', hours).replace('mm', minutes).replace('ss', seconds);
    } else {
        throw new Error('Invalid date input:' + date);
    }
}

function restArguments(func, startIndex) {
    startIndex = appUtil.isEmpty(startIndex) ? func.length - 1 : +startIndex;
    return function () {
        //length为从startIndex到最后一个参数的数量
        let length = Math.max(arguments.length - startIndex, 0),
            rest = Array(length),
            index = 0;
        //将startIndex开始的所有参数保存在rest数组中
        for (; index < length; index++) {
            rest[index] = arguments[index + startIndex];
        }
        //当startIndex <= 2时，因为大部分的参数都在rest中，所以直接使用call调用
        switch (startIndex) {
            case 0: return func.call(this, rest);
            case 1: return func.call(this, arguments[0], rest);
            case 2: return func.call(this, arguments[0], arguments[1], rest);
        }
        //将startIndex之前的参数直接保存在args数组中，将其余的参数直接保存为数组的最后一项
        let args = Array(startIndex + 1);
        for (index = 0; index < startIndex; index++) {
            args[index] = arguments[index];
        }
        //如果startIndex大于2，说明在rest之前有较多的参数，这时将参数保存在数组里，通过apply来调用函数
        args[startIndex] = rest;
        return func.apply(this, args);
    };
};

let delay = restArguments(function (func, wait, args) {
    return setTimeout(function () {
        return func.apply(null, args);
    }, wait);
});



export default appUtil;